from datetime import datetime
from threading import Thread
import time
import sys 
from ibapi.client import EClient, Contract
from ibapi.order import Order
from ibapi.wrapper import EWrapper
from ibapi.utils import iswrapper
from ibapi.scanner import ScannerSubscription
from ibapi.tag_value import TagValue
import pandas as pd
import numpy as np

class SimpleStrategy(EWrapper, EClient):
    ''' Serves as the client and the wrapper '''

    def __init__(self, addr, port, client_id):
        EWrapper.__init__(self)
        EClient.__init__(self, self)
        # 订单号
        self.order_id = 0
        # 初始化账户
        self.funds = 0.0
        # 保存价格
        self.price_list=[]
        # 保存仓位
        self.position_size = 0.0
        self.avg_cost = 0.0

        # 连接到TWS
        self.connect(addr, port, client_id)
        thread = Thread(target=self.run)
        thread.start()
        

    @iswrapper
    def currentTime(self, cur_time):
        t = datetime.fromtimestamp(cur_time)
        print('Current time: {}'.format(t))

    @iswrapper
    def scannerData(self, reqId: int, rank: int, contractDetails,
                     distance: str, benchmark: str, projection: str, legsStr: str):
        # 处理市场扫描仪数据
        super().scannerData(reqId, rank, contractDetails, distance, benchmark,
                             projection, legsStr)
        print("ScannerData. ReqId:", reqId, "Rank:", rank, "Symbol:", contractDetails.contract.symbol,
              "SecType:", contractDetails.contract.secType,
              "Currency:", contractDetails.contract.currency,
              "Distance:", distance, "Benchmark:", benchmark,
              "Projection:", projection, "Legs String:", legsStr)
        # print("ScannerData. ReqId:", reqId, ScanData(contractDetails.contract, rank, distance, benchmark, projection, legsStr))

    @iswrapper
    def scannerDataEnd(self, reqId: int):
        # 市场扫描仪获取数据结束
         super().scannerDataEnd(reqId)
         print("ScannerDataEnd. ReqId:", reqId)

    @iswrapper
    def scannerParameters(self, xml: str):
        # 处理获取市场扫描仪的参数
        super().scannerParameters(xml)
        # df = pd.read_xml(xml)
        # print(df)
        # df.to_csv("scanner.csv")
        open('scanner.txt', 'w',encoding="utf-8").write(xml)
        print("ScannerParameters received.")


    @iswrapper
    def contractDetails(self, reqId, details):
        print('Long name: {}'.format(details.longName))
        print('Category: {}'.format(details.category))
        print('Subcategory: {}'.format(details.subcategory))        
        print('Contract ID: {}\n'.format(details.contract.conId))

    @iswrapper
    def contractDetailsEnd(self, reqId):
        print('The End')

    @iswrapper
    def nextValidId(self, order_id):
        ''' Provides the next order ID '''
        self.order_id = order_id
        print('Order ID: {}'.format(order_id))

    @iswrapper
    def openOrder(self,order_id, contract, order, state):
        ''' Called in response to the submitted order '''
        print('Order status: '.format(state.status))
        print('Commission charged: '.format(state.commission))

    @iswrapper
    def orderStatus(self,order_id, status, filled, remaining, avgFillPrice, \
        permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice):
        ''' Check the status of the subnitted order '''
        print('Number of filled positions: {}'.format(filled))
        print('Average fill price: {}'.format(avgFillPrice))


        
    @iswrapper    
    def position(self, account: str, contract: Contract, position,
                  avgCost: float):
        super().position(account, contract, position, avgCost)
        print("Position.", "Account:", account, "Symbol:", contract.symbol, "SecType:",
               contract.secType, "Currency:", contract.currency,
               "Position:", position, "Avg cost:", avgCost)
        if contract.symbol=="EUR" and contract.secType=="CASH"  and contract.currency=="USD":
            self.position_size = float(position)
            self.avg_cost = avgCost

    @iswrapper
    def positionEnd(self):
        super().positionEnd()
        print("PositionEnd")


    @iswrapper
    def accountSummary(self, req_id, account, tag, value, currency):
        ''' Read information about the account '''
        print('req_id : {} Account {}: {} = {}, currency = {}'.format(req_id, account, tag, value , currency))
        if tag == 'AvailableFunds':
            print('Account {}: available funds = {}'.format(account, value))
            self.funds = float(value)


    @iswrapper
    def tickByTickMidPoint(self, reqId: int, time: int, midPoint: float):
        super().tickByTickMidPoint(reqId, time, midPoint)
        print("Midpoint. ReqId:", reqId,"Time:", datetime.fromtimestamp(time),"MidPoint:", midPoint)

        
    @iswrapper    
    def tickByTickBidAsk(self, reqId: int, time: int, bidPrice: float, askPrice: float,	bidSize, askSize, tickAttribBidAsk):
        super().tickByTickBidAsk(reqId, time, bidPrice, askPrice, bidSize,askSize, tickAttribBidAsk)
        print("BidAsk. ReqId:", reqId,"Time:", datetime.fromtimestamp(time),
            "BidPrice:", bidPrice, "AskPrice:", askPrice, "BidSize:", bidSize,"AskSize:", askSize, "BidPastLow:", 
            tickAttribBidAsk.bidPastLow,"AskPastHigh:", tickAttribBidAsk.askPastHigh)
        
    @iswrapper
    def tickByTickAllLast(self, reqId: int, tickType: int, time: int, price: float,
                           size, tickAtrribLast, exchange: str,specialConditions: str):
        super().tickByTickAllLast(reqId, tickType, time, price, size, tickAtrribLast,
                                    exchange, specialConditions)
        if tickType == 1:
            print("Last.", end='')
        else:
            print("AllLast.", end='')
            print(" ReqId:", reqId,
                "Time:", datetime.fromtimestamp(time),
                "Price:", price, "Size:", size, "Exch:" , exchange,
                "Spec Cond:", specialConditions, "PastLimit:", tickAtrribLast.pastLimit, 
                "Unreported:",tickAtrribLast.unreported)

    
    @iswrapper 
    def tickPrice(self, reqId, tickType, price: float,attrib):
        super().tickPrice(reqId, tickType, price, attrib)
        print("TickPrice. TickerId:", reqId, "tickType:", tickType,
               "Price:", price, "CanAutoExecute:", attrib.canAutoExecute,
               "PastLimit:", attrib.pastLimit, end=' ')
        
 

    @iswrapper
    def tickSize(self, reqId, tickType, size):
         super().tickSize(reqId, tickType, size)
         print("TickSize. TickerId:", reqId, "TickType:", tickType, "Size: ", size)
         
    @iswrapper
    def tickGeneric(self, reqId, tickType, value: float):
         super().tickGeneric(reqId, tickType, value)
         print("TickGeneric. TickerId:", reqId, "TickType:", tickType, "Value:", value)
    

    @iswrapper
    def realtimeBar(self, reqId, time, open, high, low, close, volume, WAP, count):
        ''' Called in response to reqRealTimeBars '''

        print('realtimeBar:{},time:{} - Opening : {},high :{},low :{},close :{},volume :{},WAP :{},count :{}'.format(reqId,datetime.fromtimestamp(time),open,high,low,close,volume,WAP,count))
        self.price_list.append(close)

    @iswrapper
    def historicalData(self, reqId:int, bar):
             print("HistoricalData. ReqId:", reqId, "BarData.", bar)
             
    @iswrapper
    def historicalDataEnd(self, reqId: int, start: str, end: str):
         super().historicalDataEnd(reqId, start, end)
         print("HistoricalDataEnd. ReqId:", reqId, "from", start, "to", end)
         
    @iswrapper
    def historicalDataUpdate(self, reqId: int, bar):
         print("HistoricalDataUpdate. ReqId:", reqId, "BarData.", bar)
         
    @iswrapper     
    def histogramData(self, reqId:int, items):
        print("HistogramData. ReqId:", reqId, "HistogramDataList:", "[%s]" % "; ".join(map(str, items)))
        
    @iswrapper      
    def historicalTicks(self, reqId: int, ticks, done: bool):
        for tick in ticks:
            print("HistoricalTick. ReqId:", reqId, tick)
    @iswrapper  
    def historicalTicksBidAsk(self, reqId: int, ticks,done: bool):
        for tick in ticks:
            print("HistoricalTickBidAsk. ReqId:", reqId, tick)
    @iswrapper  
    def historicalTicksLast(self, reqId: int, ticks,done: bool):
        for tick in ticks:
            print("HistoricalTickLast. ReqId:", reqId, tick)


    @iswrapper
    def fundamentalData(self, reqId, data):
        ''' Called in response to reqFundamentalData '''

        print('Fundamental data: ' + data)

    @iswrapper
    def error(self, req_id, code, msg):
        print('Error {}: {}'.format(code, msg))

        

def main():

    # Create the client and connect to TWS
    client = SimpleStrategy('127.0.0.1', 7497, 0)

    client.reqCurrentTime()
    # Sleep while the request is processed
    time.sleep(0.5)

    
    # client.reqContractDetails(1, contract)
    # print("self.conn",client.conn)
    # print("self.isConnected()",client.isConnected())
    time.sleep(2)
    
    # # 设置一个限价单
    # order = Order()
    # order.action = 'SELL'
    # order.totalQuantity = 20000
    # order.orderType = 'MKT'
    # # 给order获取一个有效的id
    # client.reqIds(1)
    # time.sleep(0.5)
    # # 下单
    # client.placeOrder(order_id, contract, order)
    # # 获取持仓的信息
    # client.reqPositions()
    # time.sleep(2)

    # # 获取账户的信息
    # client.reqAccountSummary(0, 'All', 'AccountType,AvailableFunds')
    # time.sleep(2)
    
    # 请求tick数据
    # print("获取bidask的数据")
    # client.reqTickByTickData(1, contract, 'BidAsk', 1, True)
    # time.sleep(5)
    # print("获取last的数据")
    # client.reqTickByTickData(2, contract, 'Last', 1, False)
    # time.sleep(5)
    # print("获取alllast的数据")
    # client.reqTickByTickData(3, contract, 'AllLast', 1, True)
    # time.sleep(10)
    # print("获取midpoint的数据")
    # client.reqTickByTickData(0, contract, 'MidPoint', 1, True)
    # time.sleep(5)

    # 请求市场数据
    # client.reqMktData(4, contract, '', False, False, [])

    # 请求bar数据
    # client.reqRealTimeBars(5, contract, 10, 'MIDPOINT', True, [])

    # 请求历史数据
    # now = datetime.now().strftime("%Y%m%d, %H:%M:%S")
    # client.reqHistoricalData(6, contract, now, '2 w', '1 day',
    #     'MIDPOINT', False, 1, False, [])
    # 请求历史直方图数据
    # client.reqHistogramData(7,contract,1,"3 days")

    # 请求历史tick数据
    # client.reqHistoricalTicks(8,contract,"20211230 21:39:33", "", 10, "TRADES", 1, True, [])
    # 请求基础数据
    # con = Contract()
    # con.symbol = 'IBM'
    # con.secType = 'STK'
    # con.exchange = 'SMART'
    # con.currency = 'USD'
    # client.reqFundamentalData(9, con, 'ReportSnapshot', [])

    # # 创建一个市场扫描仪 
    # ss = ScannerSubscription()
    # ss.instrument = 'STK'
    # ss.locationCode = 'STK.US.MAJOR'
    # ss.scanCode = 'HOT_BY_VOLUME'

    # # 增加额外的筛选标准
    # tagvalues = []
    # tagvalues.append(TagValue('avgVolumeAbove', '500000'))
    # tagvalues.append(TagValue('marketCapAbove1e6', '10'))

    # # 请求过滤后的contract
    # client.reqScannerSubscription(0, ss, [], tagvalues)
    # # 请求市场扫描仪的过滤参数
    # # client.reqScannerParameters()
    # 写一个简单的策略，当持仓为0的时候，当价格大于20周期的5秒钟的均线的时候，开多；持仓大于0的时候，当价格小于20周期的5秒钟的均线的时候平多
    
    # 设置交易的contract
    contract = Contract()
    contract.symbol = "EUR"
    contract.secType = "CASH"
    contract.currency = "USD"
    contract.exchange = "IDEALPRO"
    # 设置一个买入市价单
    buy_order = Order()
    buy_order.action = 'BUY'
    buy_order.totalQuantity = 20000
    buy_order.orderType = 'MKT'
    # 设置一个卖出市价单
    sell_order = Order()
    sell_order.action = 'SELL'
    sell_order.totalQuantity = 20000
    sell_order.orderType = 'MKT'
    client.reqRealTimeBars(5, contract, 10, 'MIDPOINT', True, [])
    # 获取账户持仓
    client.reqPositions()
    time.sleep(1)
    # 获取账户的信息
    client.reqAccountSummary(0, 'All', 'AccountType,AvailableFunds')
    time.sleep(1)
    # 设置运行1000个bar
    count = 1000
    pre_bar_num = len(client.price_list)
    while count>0:
        now_bar_num = len(client.price_list)
        client.reqCurrentTime()
        
        if now_bar_num>=5 and now_bar_num > pre_bar_num:
            count = count-1
            now_price = client.price_list[-1]
            now_avg_price = sum(client.price_list[-5:])/5
            if client.position_size<=0 and now_price>now_avg_price:
                print('now_price',now_price,"now_avg_price",now_avg_price)
                print("当前账户持仓",client.position_size,"当前bar数目",now_bar_num,"前一个bar数目",pre_bar_num,"count",count)
                print("此时应该平空开多")
                client.reqIds(1)
                client.placeOrder(client.order_id,contract,buy_order)
                print("下多单成功")
            if client.position_size>0 and now_price<now_avg_price:
                print('now_price',now_price,"now_avg_price",now_avg_price)
                print("当前账户持仓",client.position_size,"当前bar数目",now_bar_num,"前一个bar数目",pre_bar_num,"count",count)
                print("此时应该平空开多")
                client.reqIds(1)
                client.placeOrder(client.order_id,contract,sell_order)
                print("下空单成功")

            pre_bar_num = now_bar_num
        
        
        time.sleep(1)
        
    # # 休息5秒钟，等待数据返回
    # time.sleep(5)  
    # 断开连接     
    client.disconnect()

    

if __name__ == '__main__':
    main()